/*
Automatic clinic tests using a robot arm.
Copyright (C) 2010  CIC-CPC, CHU Gabriel Montpied
All other trademarks, logos and copyrights are the property
of their respective owners.

This file is part of cpc-painbot.

cpc-painbot is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

cpc-painbot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with cpc-painbot.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "widgetbras.h"
#include "ui_widgetbras.h"

#include <QGraphicsEllipseItem>
#include <QProgressDialog>
#include <QSignalMapper>
#include <QProgressBar>
#include <QMessageBox>
#include <QSettings>
#include <QDebug>
#include <QTimer>

#include "qserialdevicewatcher/serialdevicewatcher.h"

#include "commande/commande.h"
#include "commande/bras/ping.h"
#include "commande/bras/init.h"
#include "commande/bras/reponse.h"
#include "commande/bras/position.h"
#include "commande/bras/lireposition.h"
#include "commande/bras/deplacerlineaire.h"
#include "commande/bras/deplacerptp.h"

#include "bras.h"

#include "brasgraphique.h"

WidgetBras::WidgetBras(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::WidgetBras),
        attente(new QProgressDialog(this)),
        bras_(new Bras(this)),
        bras_graphique(new BrasGraphique),
        scene_bras(this)
{
    ui->setupUi(this);

    genererListePorts();
    majLabelPas();
    majLabelVitesse();

    // Icônes des boutons de contrôle du mouvement.
#if 0
    ui->pushButtonGauche->setIcon(style()->standardIcon(QStyle::SP_ArrowLeft));
    ui->pushButtonDroite->setIcon(style()->standardIcon(QStyle::SP_ArrowRight));
    ui->pushButtonMonter->setIcon(style()->standardIcon(QStyle::SP_ArrowUp));
    ui->pushButtonDescendre->setIcon(style()->standardIcon(QStyle::SP_ArrowDown));
    ui->pushButtonAvancer->setIcon(style()->standardIcon(QStyle::SP_ArrowUp));
    ui->pushButtonReculer->setIcon(style()->standardIcon(QStyle::SP_ArrowDown));
#endif

    // Icônes diverses.
    ui->pushButtonPosition->setIcon(style()->standardIcon(QStyle::SP_BrowserReload));
    attente->setWindowIcon(QIcon(":/images/bras/connection"));

    QSignalMapper *gauche_droite = new QSignalMapper(this),
    *avant_arriere = new QSignalMapper(this),
    *haut_bas = new QSignalMapper(this);

    connect(ui->pushButtonGauche, SIGNAL(clicked()), gauche_droite, SLOT(map()));
    connect(ui->pushButtonDroite, SIGNAL(clicked()), gauche_droite, SLOT(map()));

    connect(ui->pushButtonAvancer, SIGNAL(clicked()), avant_arriere, SLOT(map()));
    connect(ui->pushButtonReculer, SIGNAL(clicked()), avant_arriere, SLOT(map()));

    connect(ui->pushButtonMonter, SIGNAL(clicked()), haut_bas, SLOT(map()));
    connect(ui->pushButtonDescendre, SIGNAL(clicked()), haut_bas, SLOT(map()));

    gauche_droite->setMapping(ui->pushButtonGauche, -1);
    gauche_droite->setMapping(ui->pushButtonDroite, 1);
    avant_arriere->setMapping(ui->pushButtonAvancer, 1);
    avant_arriere->setMapping(ui->pushButtonReculer, -1);
    haut_bas->setMapping(ui->pushButtonMonter, -1);
    haut_bas->setMapping(ui->pushButtonDescendre, 1);

    connect(gauche_droite, SIGNAL(mapped(int)), this, SLOT(deplacerBrasX(int)));
    connect(avant_arriere, SIGNAL(mapped(int)), this, SLOT(deplacerBrasY(int)));
    connect(haut_bas, SIGNAL(mapped(int)), this, SLOT(deplacerBrasZ(int)));

    // Signaux émis par le bras.
    connect(bras_, SIGNAL(estPresent(bool)), this, SLOT(brasPresent(bool)));
    connect(bras_, SIGNAL(positionChangee(PositionBras)),
            this, SLOT(actualiserPosition(PositionBras)));

    connect(bras_, SIGNAL(estPret(bool)), ui->frameControles, SLOT(setEnabled(bool)));
    connect(bras_, SIGNAL(estPret(bool)), this, SLOT(ledPrete(bool)));
    connect(bras_, SIGNAL(estPresent(bool)), this, SLOT(ledPresence(bool)));

    // Gestion des erreurs.
    connect(bras_, SIGNAL(erreurTransmission()), attente, SLOT(cancel()));
    connect(bras_, SIGNAL(erreurTransmission()), this, SLOT(erreurTransmission()));
    connect(bras_, SIGNAL(erreur(int)), this, SLOT(afficherErreur(int)));


    // Signaux à destination du bras.
    connect(ui->comboBoxPort, SIGNAL(currentIndexChanged(QString)), bras_, SLOT(setPort(QString)));
    connect(ui->pushButtonConnecter, SIGNAL(clicked(bool)), bras_, SLOT(connecter(bool)));
    connect(ui->pushButtonInit, SIGNAL(clicked()), bras_, SLOT(initialiser()));
    connect(ui->pushButtonPosition, SIGNAL(clicked()), bras_, SLOT(lirePosition()));
    connect(attente, SIGNAL(canceled()), bras_, SLOT(deconnecter()));

    // Fenêtre d'attente de connexion.
    attente->setWindowTitle("Veuillez patienter");
    attente->setLabelText("Attente d'une réponse du bras...");
    attente->setMaximum(0);
    attente->setWindowModality(Qt::ApplicationModal);

    ui->graphicsViewBras->setScene(&scene_bras);
    // Cercle décrivant le rayon d'action du bras.
    // 440 de rayon, centré.
    QGraphicsItem *cercle = scene_bras.addEllipse(-440, -440, 880, 880, QPen("red"));
    scene_bras.addItem(bras_graphique);
    ui->graphicsViewBras->centerOn(cercle);

    // Sélectionne le dernier port série utilisé, s'il existe.
    // Si le port n'est plus présent, ou qu'il n'y en avait pas :
    // sélection du premier port
    QSettings params;
    int index = qMax(ui->comboBoxPort->findText(params.value("bras/port").toString()), 0);
    ui->comboBoxPort->setCurrentIndex(index);

    brasPresent(bras_->isPresent());
}

WidgetBras::~WidgetBras()
{
    // Sauvegarde des paramètres.
    QSettings params;
    params.setValue("bras/port", ui->comboBoxPort->currentText());
    delete ui;
}

void WidgetBras::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void WidgetBras::brasPresent(bool present)
{
    ui->pushButtonInit->setEnabled(present);
    ui->pushButtonConnecter->setChecked(present);

    if ( present ) {
        attente->accept();
        ui->pushButtonConnecter->setText(tr("Déconnecter"));
        ui->pushButtonConnecter->setIcon(QIcon(":/images/bras/deconnecter"));
        disconnect(ui->pushButtonConnecter, SIGNAL(clicked()), attente, SLOT(show()));
    } else {
        ui->pushButtonConnecter->setText(tr("Connecter"));
        ui->pushButtonConnecter->setIcon(QIcon(":/images/bras/connecter"));
        connect(ui->pushButtonConnecter, SIGNAL(clicked()), attente, SLOT(show()));
    }
}

void WidgetBras::ledPresence(bool present)
{
    qDebug() << "ledPresence :" << present;
    if ( present )
        ui->etatLabel->setPixmap(QPixmap(":/images/leds/orange"));
    else
        ui->etatLabel->setPixmap(QPixmap(":/images/leds/rouge"));
}

void WidgetBras::ledPrete(bool prete)
{
    qDebug() << "ledPrete :" << prete;
    if ( bras_->isPresent() ) {
        if ( prete )
            ui->etatLabel->setPixmap(QPixmap(":/images/leds/vert"));
        else
            ui->etatLabel->setPixmap(QPixmap(":/images/leds/orange"));
    }
}

void WidgetBras::actualiserPosition(const PositionBras &pos)
{
    ui->labelPosition->setText(
            tr("Position (x = %1, y = %2, z = %3, r = %4)")
            .arg(pos.x())
            .arg(pos.y())
            .arg(pos.z())
            .arg(pos.r())
            );

    ui->dialTete->setValue(- pos.r());
    bras_graphique->majPosition(pos);
}

void WidgetBras::genererListePorts()
{
    SerialDeviceWatcher *ports = new SerialDeviceWatcher(this);
    ui->comboBoxPort->clear();

    foreach ( QString port, ports->devicesAvailable() )
        ui->comboBoxPort->addItem(port);
}

void WidgetBras::rotationTeteBras()
{
    //bras_->rotationTete(-ui->dialTete->value());
}

void WidgetBras::deplacerBrasX(int sens)
{
    qreal dx = sens * ui->sliderPas->value() * ui->doubleSpinBoxPas->value();

    deplacer(dx, 0, 0, bras_->position().r() - ui->dialTete->value());
}

void WidgetBras::deplacerBrasY(int sens)
{
    qreal dy = sens * ui->sliderPas->value() * ui->doubleSpinBoxPas->value();
    deplacer(0, dy, 0, bras_->position().r() - ui->dialTete->value());
}

void WidgetBras::deplacerBrasZ(int sens)
{
    qreal dz = sens * ui->sliderPas->value() * ui->doubleSpinBoxPas->value();
    deplacer(0, 0, dz, bras_->position().r() - ui->dialTete->value());
}

void WidgetBras::deplacer(qreal dx, qreal dy, qreal dz, qreal dr)
{
    float vitesse = ui->dialVitesse->value() * ui->doubleSpinBoxVitesse->value();
    //ledPrete(false);
    bras_->deplacer(dx, dy, dz, dr, vitesse);
}

void WidgetBras::erreurTransmission()
{
    QMessageBox::critical(
            this,
            tr("Erreur de Transmission"),
            tr("Le Bras ne répond pas !\nVérifiez la connection.")
    );
}

void WidgetBras::afficherErreur(int type)
{
    using namespace commande::bras;
    QString titre, message;

    switch (type) {
    case Reponse::Init:
        titre = tr("Erreur à l'initialisation");
        message = tr("Vérifier que le bouton 'power on'"
                     "du boîtier de contrôle est allumé.\n"
                     "Le cas échéant, appuyez dessus.");
        break;
    case Reponse::DeplacementeLineaire:
        titre = tr("Erreur de déplacement");
        message = tr("Le déplacement spécifié est invalide.");
        break;

    default:
        titre = tr("Erreur");
        message = tr("La commande envoyée a entraîné une erreur.");
    }

    QMessageBox::critical(this, titre, message);
}

void WidgetBras::majLabelPas()
{
    QString texte = tr("Pas de déplacement (%1 mm)")
                    .arg(ui->sliderPas->value() * ui->doubleSpinBoxPas->value() );
    ui->labelPas->setText(texte);
}

void WidgetBras::majLabelVitesse()
{
    ui->labelVitesse->setText(tr("Vitesse (%1 mm/s)")
                              .arg(ui->dialVitesse->value() * ui->doubleSpinBoxVitesse->value())
                              );
}
